#include "files/file.h"
#include "xml/xml_attributes.h"
#include "xml/xml_parser.h"

#include <expat.h>
#include <fstream>

namespace Tvimeg
{
	class XMLParser::XMLParserData
	{
	public:
		XMLParserData(void);
		~XMLParserData(void);

		XML_Parser&		GetParser(void);

	private:
		XML_Parser		parser;
	};

	XMLParser::XMLParserData::XMLParserData(void)
	{
		parser = XML_ParserCreate(NULL);
	}

	XMLParser::XMLParserData::~XMLParserData(void)
	{
		XML_ParserFree(parser);
	}

	XML_Parser& XMLParser::XMLParserData::GetParser(void)
	{
		return parser;
	}

	XMLParser::XMLParser(void)
	{
		parserData = new XMLParserData;

		XML_SetUserData(parserData->GetParser(), this);
		XML_SetElementHandler(parserData->GetParser(), StartElementHandler, EndElementHandler);
		XML_SetCharacterDataHandler(parserData->GetParser(), CharacterDataHandler);
	}

	XMLParser::~XMLParser(void)
	{
		delete parserData;
	}

	bool XMLParser::Parse(const File& file)
	{
		bool result = true;

		std::ifstream stream(file.GetPath().c_str(), std::ios_base::in | std::ios_base::binary);
		while (stream.good() && result)
		{
			const int data_size = 64 * 1024;
			char data[data_size];
			stream.read(data, data_size);

			result = XML_Parse(parserData->GetParser(), data, stream.gcount(), !stream.good()) == XML_STATUS_OK;
		}

		return result;
	}

	void XMLParser::StartElementHandler(void* parser, const char* elementName, const char** attributes)
	{
		XMLParser* xmlParser = static_cast<XMLParser*>(parser);
		if (xmlParser)
		{
			const std::string name(elementName);
			const XMLAttributes xmlAttributes(attributes);
			xmlParser->StartElement(name, xmlAttributes);
		}
	}

	void XMLParser::EndElementHandler(void* parser, const char* elementName)
	{
		XMLParser* xmlParser = static_cast<XMLParser*>(parser);
		if (xmlParser)
		{
			const std::string name(elementName);
			xmlParser->EndElement(name);
		}
	}

	void XMLParser::CharacterDataHandler(void* parser, const char* string, int length)
	{
		XMLParser* xmlParser = static_cast<XMLParser*>(parser);
		if (xmlParser)
		{
			const std::string string_value(string, length);
			xmlParser->CharacterData(string_value);
		}
	}
}
